<?php
error_reporting(E_ALL);

if(!isset($relPath)) $relPath = "./";
if(! isset($db_server))
{
    require_once $relPath."udb_user.php";
}

class DpDb
{
    var $_is_echo_queries   = false;
    var $_is_log_queries;
    var $_is_time_queries;
    var $_mysqli;
    /** @var $_result mysqli_result */
    var $_result;
    var $_error_msg = "";
	
    public function __construct($islog = false, $istime = false) {
        global $db_server, $db_user, $db_password, $db_name;
        $this->_is_log_queries = $islog ;
        $this->_is_time_queries = $istime ;

    	$this->_mysqli = new mysqli("p:".$db_server, $db_user,
                                    $db_password, $db_name);
        if($this->_mysqli->connect_errno) {
            $this->_error_msg = $this->_mysqli->connect_error;
        }
    }

    public function __destruct() {
    }

    private function sql_select($sql) {
        if($this->_is_echo_queries) {
            echo PrettySql($sql);
        }
        if($this->_is_time_queries) {
            global $marktime;
            $marktime = microtime(true);
        }
        $this->_mysqli->query("SET NAMES 'utf8'");

        $this->_result = $this->_mysqli->query($sql);
        // if($this->IsError())     // printed again in caller
            // echo PrettySql($sql);
        return $this->_result;
    }

    private function sql_close() {
        $this->_mysqli->close();
    }

    public function ErrorMessage() {
        return $this->_error_msg;
    }

    public function SqlRows($sql) {
        $rows = array();
        $result = $this->sql_select($sql);
        if($this->IsError()) {
            echo PrettySql($sql);
            die($this->ErrorMessage());
        }
        while($row = $result->fetch_array()) {
            $rows[] = $row;
        }
        $result->free();                     
        $this->DrainResults();
        return $rows;
    }

    public function SqlObjects($sql) {
        $objects = array();
        $result = $this->sql_select($sql);
        if($this->IsError()) {
            echo PrettySql($sql);
            die($this->ErrorMessage());
        }
        while($object = $this->_result->fetch_object()) {
            $objects[] = $object;
        }
        $result->free();  
        $this->DrainResults();
        return $objects;
    }

    private function DrainResults() {
        while ($this->_mysqli->next_result()) {
            $result = $this->_mysqli->use_result();
            if ($result instanceof mysqli_result) {
                $result->free();
            }
        }
    }

    public function SqlOneObject($sql) {
        $result = $this->sql_select($sql);
        if($this->IsError()) {
            echo PrettySql($sql);
            die($this->ErrorMessage());
        }
        $obj = $result->fetch_object();
        $result->free();
        $this->DrainResults();
        return $obj;
    }
    
    public function SqlOneRow($sql) {
        $result = $this->sql_select($sql); 
        if($this->IsError()) {
            echo PrettySql($sql);
            die($this->ErrorMessage());
        }
        if(! is_object($result))
            say($this->ErrorMessage());
        if($result->num_rows > 0)
            $row = $result->fetch_array(); 
        else
            $row = null;
        $result->free();                  
        $this->DrainResults();
        return $row;
    }

    public function SqlValues($sql) {
        $objects = array();
        $result = $this->sql_select($sql);      
        if($this->IsError()) {
            echo PrettySql($sql);
            die($this->ErrorMessage());
        }
        while($row = $result->fetch_array()) { 
            $objects[] = $row[0];
        }
        $result->free();                      
        $this->DrainResults();
        return $objects;
    }

    public function SqlOneValue($sql) {
        $result = $this->sql_select($sql); 
        if(!empty($result)) {
            $row = $result->fetch_array();
        }
        $result->free();                 
        $this->DrainResults();
        return empty($row) ? null : $row[0] ;
    }

    public function SqlExecute($sql) {
        // global $db_server, $db_user, $db_password, $db_name;
        global $marktime;
        if($this->_is_log_queries) {
            $this->Log("SqlExecute: 
                $sql");
        }
        if($this->_is_time_queries) {
            global $marktime;
            $marktime = microtime(true);
        }
        if($this->IsEchoing()) {
            echo PrettySql($sql);
        }
        $this->_result = $this->_mysqli->query($sql);   
        $ret = $this->affected_rows();                         
        if($this->_is_log_queries) {
            $echotime = sprintf(" Build Time: %5.3f ",
                            microtime(true) - $marktime);
            $this->Log("Execution time: $echotime");
        }
        return $ret;
    }

    private function affected_rows() {
        return $this->_mysqli->affected_rows;
    }

    private function IsError() {
        if(empty($this->_mysqli))
            return false;
        return !empty($this->_mysqli->error);
    }

    // arguments are sql and an array of 
    // argument references (not values).
    public function SqlExecutePS($sql, $args) {
        global $marktime;

        if($this->_is_log_queries) {
            $this->Log("SqlExecutePS: $sql");
        }
        if($this->_is_time_queries) {
            global $marktime;
            $marktime = microtime(true);
        }
        if($this->IsEchoing()) {
            echo PrettySql($sql);
        }
        $stmt = $this->_mysqli->prepare($sql);

        // make the type-string
        $typestr = make_typestring($args);
        $params = array($typestr);
        $params = array_merge($params, $args);
        
        call_user_func_array(array($stmt, 'bind_param'), $params);
        $stmt->execute();

        $ret = $this->affected_rows();
        $stmt->close();
        if($this->_is_log_queries) {
            $echotime = sprintf(" Build Time: %5.3f ",
                                microtime(true) - $marktime);
            $this->Log("Execution time: $echotime");
        }
        return $ret;
    }

    public function SqlExists($sql) {
        $result = $this->SqlOneRow($sql);
        return !empty($result[0]);
    }

    public function Log($text) {
        global $User;
        if(! $this->_mysqli) {
            assert(false);
            return;
        }
        $username = empty($User)
            ? "(unavailable)"
            : $User->Username();

        echo html_comment( $text ) ;
        $errormsg = $this->ErrorMessage();

        $sql = "INSERT INTO log (
                    username,
                    eventtime,
                    logtext,
                    errormsg)
                VALUES (
                    '$username',
                    UNIX_TIMESTAMP(),
                    '$text',
                    '$errormsg')";
        $this->_mysqli->query($sql); 
        if($this->_mysqli->error) {
            say($this->_mysqli->error);
        }
    }

    public function Ping() {
        return "DpDb ping";
    }

    public function Info() {
        return $this->_mysqli->info;
    }

    public function SetEchoing() {
        $this->_is_echo_queries = true;
    }

    public function IsEchoing() {
        return $this->_is_echo_queries;
    }

    public function ClearEchoing() {
        $this->_is_echo_queries = false;
    }

    public function SetTiming() {
        $this->_is_time_queries = true;
    }

    public function ClearTiming() {
        $this->_is_time_queries = false;
    }

    public function SetLogging() {
        $this->_is_log_queries = true;
    }

    public function ClearLogging() {
        $this->_is_log_queries = false;
    }
    public function FieldList($tablename) {
        $sql = "SELECT * 
                FROM information_schema.columns
                WHERE table_name = '$tablename'";
        $objs = $this->SqlObjects($sql);
        $ary = array();

        foreach($objs as $key => $value) {
            $ary[$value->COLUMN_NAME] = $value;
        }
        return $ary;
    }
}

function make_typestring($args) {
    assert(is_array($args));
    $ret = "";
    foreach($args as $arg) {
        switch(gettype($arg)) {
            case "boolean":
            case "integer":
                $ret .= "i";
                break;
            case "double":
                $ret .= "d";
                break;
            case "string":
                $ret .= "s";
                break;
            case "array":
            case "object":
            case "resource":
            case "NULL":
            default:
                // call it a blob and hope
                // you know what you're doing.
                $ret .= "b";
                break;
        }
    }
    return $ret;
}
function refValues($arr) {
    $refs = array();
    foreach($arr as $key => $value)
        $refs[$key] = &$arr[$key];
    return $refs;
} 

?>
